package com.hdu;


import lombok.extern.slf4j.Slf4j;

import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 阻塞队列
 */
@Slf4j
public class BlockingQueue<E> {


    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> r = new BlockingQueue<Integer>(3);

        // 创建5个线程放置
        for (int i = 0; i < 5; i++) {
            int j = i + 1;
            new Thread(() -> {
                r.put(j);
            }).start();
        }

        TimeUnit.SECONDS.sleep(1);


        // 创建七个线程取
        for (int i = 0; i < 7; i++) {
            new Thread(() -> {
                r.take();
            }).start();
        }
    }


    private final Deque<E> DEQUE = new LinkedList<>();
    private final ReentrantLock MAIN_LOCK = new ReentrantLock();
    private final Condition WAIT_NOT_EMPTY = MAIN_LOCK.newCondition();
    private final Condition WAIT_NOT_FULL = MAIN_LOCK.newCondition();
    private final int CAPACITY;


    public BlockingQueue(int capacity) {
        this.CAPACITY = capacity;
    }

    public void put(E e) {
        try {
            MAIN_LOCK.lock();
            while (DEQUE.size() == CAPACITY) {
                log.info("队列已满，等待有位置可以放置元素");
                WAIT_NOT_FULL.await();
            }
            log.info("放置元素, e : {}", e);
            DEQUE.addLast(e);
            WAIT_NOT_EMPTY.signal();
        } catch (Exception ignored) {

        } finally {
            MAIN_LOCK.unlock();
        }
    }

    public E take() {
        try {
            MAIN_LOCK.lock();
            while (DEQUE.size() == 0) {
                log.info("等待有元素...");
                WAIT_NOT_EMPTY.await();
            }
            E e = DEQUE.removeFirst();
            log.info("取走元素.. e : {}", e);
            WAIT_NOT_FULL.signal();
            return e;
        } catch (Exception exception) {
            return null;
        } finally {
            MAIN_LOCK.unlock();
        }
    }

}
